﻿using Interface;
using Model;
using SqlServer;
using System;
using System.Reflection;

namespace ConsoleApp1
{
    class Program
    {
        /// <summary>
        /// 反射：Reflection  
        /// using System.Reflection 命名空间时是微软提供给的一个帮助类库 
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World-----反射!");
            try
            {
                // 一

                #region 常规 普通写法
                ////工作中最长用得 就是  实例化对象 调用 方法

                ////创建对象   ：接口声明实例化接口实现

                //SqlServerHelper dBHelper = new SqlServerHelper();
                ////调用方法
                //dBHelper.Query();

                #endregion

                #region 反射 实例化对象 


                //动态加载类型
                Assembly assembly = Assembly.Load("SqlServer");//dll名称  默认当前目录下查找 exe 所在件夹中

                //TODO:这个方法通过程序集的长名称（包括程序集名，版本信息，语言文化，公钥标记）来加载程序集的，会加载此程序集引用的其他程序集，一般情况下都应该优先使用 这个方法，他的执行效率比LoadFrom要高很多


                //根据全路径去加载 全名称= 全路径+dll名称 + 后缀
                Assembly assembly1 = Assembly.LoadFile(@"C:\Users\VULCAN\Desktop\.NET13期练习\反射\ConsoleApp1\ConsoleApp1\bin\Debug\net5.0\SqlServer.dll");

                //TODO:这个方法是从指定的文件来加载程序集，和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集
                //例如:Assembly.LoadFile只载入相应的dll文件，比如Assembly.LoadFile("abc.dll")，则载入abc.dll，假如abc.dll中引用了def.dll的话，def.dll并不会被载入


                // 全名称
                Assembly assembly2 = Assembly.LoadFrom("SqlServer.dll");
                Assembly assembly3 = Assembly.LoadFrom(@"C:\Users\VULCAN\Desktop\.NET13期练习\反射\ConsoleApp1\ConsoleApp1\bin\Debug\net5.0\SqlServer.dll");
                //TODO:Assembly.LoadFrom则不一样，它会载入dll文件及其引用的其他dll，比如上面的例子，def.dll也会被载入

                //输出 
                foreach (Type type in assembly.GetTypes())//获取类型 
                {
                    Console.WriteLine(type.Name);//类名  
                    foreach (MethodInfo method in type.GetMethods())//获取方法 其中会输出 objet 方法  因为是是所有基类的父类  
                    {
                        Console.WriteLine(method.Name);
                    }
                }

                //参考文献：https://www.cnblogs.com/zagelover/articles/2726034.html

                #endregion

                #region 反射 调用方法 
                // //动态加载类型
                // Assembly toAssembly = Assembly.Load("SqlServer");//dll名称  默认当前目录下查找 exe 所在件夹中
                // //获取类型
                // Type toType = toAssembly.GetType("SqlServer.SqlServerHelper"); //命名空间+类名

                // //1
                // dynamic dDbHelper = Activator.CreateInstance(toType);//使用指定类型的无参数构造函数创建指定类型的实例
                // dDbHelper.Query();//dynamic 是一个动态类型，可以避开编译器的检查，运行时检查  存在安全问题 

                // //2 
                // //创建对象
                // object oDbHelper = Activator.CreateInstance(toType);//使用指定类型的无参数构造函数创建指定类型的实例
                // //调用方法
                //// oDbHelper.Query();//  编译器就不认可  c# 是强类型语言，不能Query 


                // IDBHelper dBHelper1 = oDbHelper as IDBHelper;
                // dBHelper1.Query();

                // //封装  包一成
                // IDBHelper iDBHelper = SimlpFactory.CreateInstentce();
                // iDBHelper.Query();

                #endregion

                #region 反射测试

                ////反射选择不同的构造函数创建对象
                //Assembly threeAssembly = Assembly.Load("SqlServer");//dll名称  默认当前目录下查找 exe 所在件夹中

                ////获取类型
                //Type threeType = threeAssembly.GetType("SqlServer.ReflectionTest"); //命名空间+类名

                ////创建对象
                //object oDbHelper1 = Activator.CreateInstance(threeType, new object[]
                //{
                //});

                //object oDbHelper2 = Activator.CreateInstance(threeType, new object[]
                //{
                //     "我是谁"
                //});

                //object oDbHelper3 = Activator.CreateInstance(threeType, new object[]
                //{
                //     123
                //});
                #endregion

                // 二

                #region 反射 调用普通方法

                ////调用方法为什么一定要类型转换？ 
                ////需要先获取方法，然后方法Invoke
                //Assembly fourAssembly = Assembly.Load("SqlServer");//dll名称  默认当前目录下查找 exe 所在件夹中
                ////反射获取类型
                //Type fourType = fourAssembly.GetType("SqlServer.ReflectionTest"); //命名空间+类名
                //object oTest = Activator.CreateInstance(fourType);//反射创建对象

                //MethodInfo show1 = fourType.GetMethod("Show1"); //获取方法
                ////show1.Invoke(oTest, new object[0]);//反射调用方法  无参
                //show1.Invoke(oTest, null);//反射调用方法  无参

                //MethodInfo show2 = fourType.GetMethod("Show2");//获取方法
                //show2.Invoke(oTest, new object[] { 2 });//反射调用方法  有参 需要参数的时候，参数类型必须和方法参数类型保持一致

                ////重载方法
                //MethodInfo show3 = fourType.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });  //限定参数类型 便于重载
                //show3.Invoke(oTest, new object[] { "哈哈", 134 });// 反射调用方法 

                ////私有方法
                //MethodInfo show4 = fourType.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);  //指定要在搜索中包含的实例成员  |指定搜索中包含的非公共成员。 
                //show4.Invoke(oTest, new object[] { "嘻嘻" });// 反射调用方法 

                ////静态方法
                //MethodInfo show5 = fourType.GetMethod("Show5");//获取方法
                //show5.Invoke(oTest, new object[] { "恩恩" });// 反射调用静态方法： 对象的实例  可以传入，也可以传入null
                //show5.Invoke(null, new object[] { "好的" });


                #endregion

                #region 反射调用 泛型方法

                //////正常调用  泛型 延时声明
                //GenericMethod genericMethod = new GenericMethod();
                //genericMethod.Show<int, string, DateTime>(1234, "", DateTime.Now);//调用时候 明确类型

                ////反射 调用
                //Assembly fiveAssembly = Assembly.Load("SqlServer");//dll名称  默认当前目录下查找 exe 所在件夹中
                //                                                   //反射获取类型
                //Type fiveType = fiveAssembly.GetType("SqlServer.GenericMethod"); //命名空间+类名
                //object fiveTest = Activator.CreateInstance(fiveType);//反射创建对象
                //MethodInfo fiveShow = fiveType.GetMethod("Show"); //获取方法
                //MethodInfo fiveGenericMethod = fiveShow.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) });// 指定泛型方法的  具体参数 
                //fiveGenericMethod.Invoke(fiveTest, new object[] { 123, "测试", DateTime.Now }); // 传入参数必须和声明的一样
                #endregion

                #region  在泛型类型中 调用泛型方法

                // //正常调用
                //GenericClass<int, string, DateTime> genericClass = new GenericClass<int, string, DateTime>();
                //genericClass.Show(1, "2", DateTime.Now);

                ////反射 调用
                //Assembly sixAssembly = Assembly.Load("SqlServer");//dll名称  默认当前目录下查找 exe 所在件夹中
                ////反射获取类型
                //Type sixType = sixAssembly.GetType("SqlServer.GenericClass`3"); //命名空间+类名  泛型类 需要占位符
                //Type sixType1 = sixType.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) });// 指定参数类型
                //object sixTest = Activator.CreateInstance(sixType1);//反射创建对象
                //MethodInfo sixShow = sixType1.GetMethod("Show"); //获取方法
                //sixShow.Invoke(sixTest, new object[] { 123, "反射", DateTime.Now });

                #endregion

                #region  结合使用    --及是泛型类  方法又是泛型方法   自己扩展
                //GenericDouble<int> toGenericClass = new GenericDouble<int>();
                //toGenericClass.Show(1, "2", DateTime.Now);
                //Assembly sevenAssembly = Assembly.Load("SqlServer");//dll名称  默认当前目录下查找 exe 所在件夹中
                //Type sevenType = sevenAssembly.GetType("SqlServer.GenericDouble`1"); //命名空间+类名  泛型类 需要占位符

                //Type sevenType1 = sevenType.MakeGenericType(new Type[] { typeof(int) });// 指定参数类型
                //object sevenTest = Activator.CreateInstance(sevenType1);//反射创建对象
                //MethodInfo sevenShow = sevenType1.GetMethod("Show"); //获取方法
                //MethodInfo sevenGenericMethod = sevenShow.MakeGenericMethod(new Type[] { typeof(int), typeof(string) });// 指定泛型方法的  具体参数 
                ////这里会报错  
                //sevenGenericMethod.Invoke(sevenTest, new object[] { 123, "1", DateTime.Now.ToString() }); // 传入参数必须和声明的一样
                #endregion

                #region 总结
                //反射特点：
                //动态 减少对象和对象之间的依赖，只需要知道类名(字符串)、方法名(字符串)，就可以调用，
                //还可以突破特定权限，可以做到普通方式无法做到的
                //编写比较困难，代码量大，编写的时候容易出错 
                //性能问题，性能损耗大,经过测试：发现反射确实性能损耗比较大  对于性能损耗，大家要理性看待，因为执行的数量巨大，中间的这些性能损耗是可以忽略不计


                //测试性能
                /*   Monitor.Show()*/
                ;   //其实可以 优化  还得注重代码优化



                #endregion

                #region 反射实体 属性字段

                ////正常使用
                //People people = new People()
                //{
                //    Id = 123,
                //    Name = "测试",
                //    Description = "学习"
                //};

                //Console.WriteLine($"people.Id={people.Id}");
                //Console.WriteLine($"people.Name={people.Name}");
                //Console.WriteLine($"people.Description={people.Description}");
                ////反射怎么玩呢？


                //// 使用Dll获取类类型和方法

                ////获取类型
                //Type eightType = typeof(People);
                ////使用指定类型的无参数构造函数创建指定类型的实例。
                //object oPeople = Activator.CreateInstance(eightType);
                ////返回当前System.Type的所有公共属性。数组
                ////获取属性
                //foreach (PropertyInfo prop in eightType.GetProperties())
                //{
                //    if (prop.Name.Equals("Id"))
                //    {
                //        prop.SetValue(oPeople, 456);
                //    }
                //    else if (prop.Name.Equals("Name"))
                //    {
                //        prop.SetValue(oPeople, "小明");
                //    }
                //    Console.WriteLine($"oPeople.{prop.Name}={prop.GetValue(oPeople)}");
                //}
                ////获取字段
                //foreach (FieldInfo field in eightType.GetFields())
                //{
                //    if (field.Name.Equals("Description"))
                //    {
                //        field.SetValue(oPeople, "备注");
                //    }
                //    Console.WriteLine($"oPeople.{field.Name}={field.GetValue(oPeople)}");
                //}

                #endregion

            }
            catch (Exception ex)
            {

                throw ;
            }
        }
    }
}
